﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Security.Cryptography;

namespace GoFishWeb.Models
{
    public class Deck
    {
        public Deck()
        {
            // needed for entity framework.  Must be no parameters and can not be private
        }

        [Key, ForeignKey("Game")]
        public int Id { get; set; }

        public virtual List<Card> Cards { get; set; }

        public virtual GoFishGame Game { get; set; }

        public bool IsInitialized { get; set; }

        public bool IsEmpty
        {
            get { return !Cards.Any(); }
        }

        /// <summary>
        ///     Returns the count of how many cards are left in the deck
        /// </summary>
        public int Count
        {
            get { return Cards.Count; }
        }

        public void InitializeDeck()
        {
            if (!IsInitialized)
            {
                Cards = new List<Card>();
                for (var suit = 0; suit <= 3; suit++)
                    for (var value = 1; value <= 13; value++)
                        Cards.Add(new Card((CardSuit) suit, (CardRank) value));
            }
            IsInitialized = true;
        }


        /// <summary>
        /// </summary>
        /// <returns>
        ///     IndexOutOfRangeException if there are no more cards left
        /// </returns>
        public Card DealCard()
        {
            if (Cards.Count == 0)
                throw new IndexOutOfRangeException("No more cards left in the deck.");

            var card = Cards.First();
            Cards.RemoveAt(0);

            return card;
        }

        /// <summary>
        /// </summary>
        /// <param name="cardPosition">Position of the card to retrieve.  0 based</param>
        /// <returns>
        ///     IndexOutOfRangeException if the card position is not within the range of cards left
        /// </returns>
        public Card GetCard(int cardPosition)
        {
            if (cardPosition < 0 || cardPosition > Cards.Count)
                throw new IndexOutOfRangeException("Invalid card choice.");

            var card = Cards.ElementAt(cardPosition);
            Cards.RemoveAt(cardPosition);

            return card;
        }


        /// <summary>
        ///     Randomizes the deck order
        /// </summary>
        public void Shuffle()
        {
            var provider = new RNGCryptoServiceProvider();
            var n = Cards.Count;
            while (n > 1)
            {
                var box = new byte[1];
                do provider.GetBytes(box);
                while (!(box[0] < n * (Byte.MaxValue / n)));
                var k = (box[0] % n);
                n--;
                var value = Cards[k];
                Cards[k] = Cards[n];
                Cards[n] = value;
            }
        }

    }
}